package com.sixth;

/**
 * 当多个线程共享同一个数据，会出现线程安全问题
 * JAVA 的多线程是使用抢占式来实现
 * 对象锁：JAVA 在设计时把锁的概念定义在对象身上，而不是线程
 * 生产者与消费者是 互斥关系
 * 对象设置锁，就是线程开发中的同步
 * 在此示例中，我们需要达到的效果是：两个线程在各自调用同一个 Food对象的不同方法时，
 * 要限制两个方法以互斥的方式进行，比如，A 线程调用了set方法，那么 B 线程必须等待 A 线程完成set方法的调用后
 * 才能执行，相反是一样的。
 * 应用场景：一个是负责提供数据的线程，把数据拿到后放到队列中，另一个线程不断的从队列中取出数据进行操作
 */
public class ProductConsumerDemo {
    public static void main(String[] args) {
        Food food = new Food();
        Product p = new Product(food);
        Consumer c = new Consumer(food);
        Thread pt = new Thread(p);
        Thread ct = new Thread(c);
        pt.start();
        ct.start();
    }
}

/**
 * 生产者
 */
class Product implements Runnable {
    private Food food;

    Product(Food food) {
        this.food = food;
    }

    @Override
    public void run() {
        //模拟生产者不断的生产食物
        //一共生产30份菜，交替生产两种菜
        for (int i = 0; i < 30; i++) {
            if (i % 2 == 0) {
                food.set("佛跳墙", "闽菜");
            } else {
                food.set("白切鸡", "粤菜");
            }
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable {
    private Food food;

    Consumer(Food food) {
        this.food = food;
    }

    @Override
    public void run() {
        //消费30份食物
        for (int i = 0; i < 30; i++) {
            food.get();
        }
    }
}


//食物类
class Food {
    private String name;  //食物名
    private String desc; //食物描述
    private boolean flag = true; //true 表示可以生产，不能消费，false 表示可以消费，不能生产
    //Object obj = new Object();// 只是作为锁来用

    /**
     * 存值（生产产品）
     */
    void set(String name, String desc) {
        synchronized (this) { //在对象上上锁
            if (!flag) { //表示不能生产，所以就让出 CPU，并释放对象锁
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //表示正在生产
            this.name = name;
            try {
                //模拟生产食物的时间过程
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.desc = desc;
            flag = false;
            this.notify(); // 唤醒在此对象锁上的任意一个线程
        }
    }

    /**
     * 取值（消费产品）
     */
    void get() {
        synchronized (this) {
            if (flag) {  // 表示不能消费
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //正在消费
            System.out.println(this.name + "--" + this.desc);
            flag = true;
            this.notify(); //唤醒在此对象锁上的任意一个线程
        }
    }

    Food() {
    }

    public Food(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
